/**
 * @file BSP_Init.c
 * @author carl.pan (1586975697@qq.com)
 * @brief
 * @version 0.1
 * @date 2025-06-28
 *
 * @copyright Copyright (c) 2025
 *
 */

#include <stdio.h>
#include "bsp_init.h"
#include "stm32f4xx.h"

#include "delay.h"

/**
 * @brief Rcc_enable all
 *
 */
static void RCC_Enable(void)
{
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); // enable GPIO
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); // enable GPIO
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE); // enable GPIO
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE); // enable GPIO
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE); // enable GPIO
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE); // enable GPIO
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE); // enable GPIO
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOH, ENABLE); // enable GPIO
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOI, ENABLE); // enable GPIO

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); // enable USART
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); // enable USART
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE); // enable USART
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART4, ENABLE);  // enable USART
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART5, ENABLE);  // enable USART
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART6, ENABLE); // enable USART

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE); // enable DMA1
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE); // enable DMA2

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); // enable SYSCFG
}

/*-------------------------------------------TIME-------------------------------------------------*/

typedef struct
{
    volatile uint32_t hour;
    volatile uint32_t minute;
    volatile uint32_t s;
} t_SystemTime;

/**
 * @brief system time
 *
 */
// static t_SystemTime sysTime_zero = {0, 0, 0};
static t_SystemTime sysTime = {0, 0, 0};

void TIM3_IRQHandler(void)
{
    if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) // 检查指定的TIM中断发生与否:TIM 中断源
    {
        TIM_ClearITPendingBit(TIM3, TIM_IT_Update); // 清除TIMx的中断待处理位:TIM 中断源
        sysTime.s++;
        if (sysTime.s >= 60)
        {
            sysTime.s = 0;
            sysTime.minute++;
            if (sysTime.minute >= 60)
            {
                sysTime.minute = 0;
                sysTime.hour++;
            }
        }
    }
}

void TIM3_Init(void) // TIM3计时中断周期:1ms
{
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); // 时钟使能

    TIM_TimeBaseStructure.TIM_Period = 999999;                  // 1s是一个完整周期
    TIM_TimeBaseStructure.TIM_Prescaler = 83;                   // 时钟频率变成了 1Mhz，1us跳一下
    TIM_TimeBaseStructure.TIM_ClockDivision = 0;                // 设置时钟分割:TDTS = Tck_tim
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // TIM向上计数模式
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); // 根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位

    TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
    NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; // TIMx中断

    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 6; // 先占优先级
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;        // 从优先级
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;           // IRQ通道被使能
    NVIC_Init(&NVIC_InitStructure); // 根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器

    TIM_Cmd(TIM3, ENABLE); // 使能TIMx外设
}

static uint32_t getSysTime_ms(void)
{
    return TIM3->CNT / 1000;
}

static uint32_t getSysTime_us(void)
{
    return TIM3->CNT % 1000;
}

/**
 * @brief Obtain the system's running time accurate to e_SystemTime_Type.
 *
 * @param time
 * @return uint32_t
 */
uint64_t getSysTime(e_SystemTime_Type type)
{
    uint64_t time = 0;
    switch (type)
    {
        case TIME_TYPE_HOUR:
            time = sysTime.hour;
            break;
        case TIME_TYPE_MINUTE:
            time = sysTime.minute + (uint64_t)sysTime.hour * 60;
            break;
        case TIME_TYPE_S:
            time = sysTime.s + (sysTime.minute + (uint64_t)sysTime.hour * 60) * 60;
            break;
        case TIME_TYPE_MS:
            time = getSysTime_ms() + (sysTime.s + (sysTime.minute + (uint64_t)sysTime.hour * 60) * 60) * 1000;
            break;
        case TIME_TYPE_US:
            time = getSysTime_us() +
                   (getSysTime_ms() + (sysTime.s + (sysTime.minute + (uint64_t)sysTime.hour * 60) * 60) * 1000) * 1000;
            break;
    }
    return time;
}

/*------------------------------------------TIME END----------------------------------------------*/

/**
 * @brief hard ware init
 * Initialize the necessary modules before system startup.
 * Do not call operating system API, because cheduler is not start at this time;
 */
static void prvSetupHardware(void)
{
    /* Setup STM32 system (clock, PLL and Flash configuration) */
    SystemInit();

    /* Ensure all priority bits are assigned as preemption priority bits. */
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);

    /* Enable peripheral RCC */
    RCC_Enable();

    /* Configure HCLK clock as SysTick clock source. */
    SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);

    /* init systick */
    SysTick_Init();
}

/**
 * @brief init bsp
 *
 */
void bsp_init(void)
{
    prvSetupHardware();
}
